#include "head.cuh"

int main(void){
	printf("Now: %s\n", UIviewFileName);

	Params params;
	params.split_rate = 0.8f;

	float *UIview = NULL;
	float *IUview = NULL;
	float *UIview_test = NULL;
	std::stringstream ss;
	float* d_UIview = NULL;
	float* d_anchors_of_uiview = NULL;
	float *anchors_of_uiview = NULL;
	float *uZ = NULL;
	cublasHandle_t cublashandle;
	float *d_IUview = NULL;
	float *d_anchors_of_iuview = NULL;
	float *anchors_of_iuview = NULL;
	float *iZ = NULL;
	float *d_S_u = NULL;
	float *h_S_u = NULL;
	float *d_S_i = NULL;
	float *h_S_i = NULL;

	int dev = 0;
	cudaError_t cudaStatus = cudaSetDevice(dev);
	if (cudaStatus != cudaSuccess) {
		fprintf(stderr, "cudaSetDevice failed!  Do you have a CUDA-capable GPU installed?");
		system("pause"); exit(1);
	}
	cudaDeviceProp deviceProp;
	CHECK_CUDA(cudaGetDeviceProperties(&deviceProp, dev));
	printf("using Device %d:%s\n", dev, deviceProp.name);

	clock_t start_of_main = clock();

	////////////////////////////////////////////////////////////////////////////////////////
	//// UIview: User-Item rating matrix for training
	//// UIview_test: same dimension as UIview for testing score
	//// UUview: User-User friendship matrix which has all-ones diag and is symmetric
	////////////////////////////////////////////////////////////////////////////////////////
	
	UIview = (float*)malloc(nUsers*nItems*sizeof(float));
	CHECK_PARAM(UIview != NULL, "allocate memory space for UIview failed.");
	IUview = (float*)malloc(nItems*nUsers*sizeof(float));
	CHECK_PARAM(IUview != NULL, "allocate memory space for IUview failed.");
	//UIview_test = (float*)malloc(nUsers*nItems*sizeof(float));
	//CHECK_PARAM(UIview_test != NULL, "allocate memory space for UIview_test failed.");
	//
	//CHECK_PARAM(read_data_2_matrix(UIview, UIview_test, params.split_rate, IUview), "read data set failed.");
	//
	//ss.str("");
	//ss << nUsers << "_" << nItems << "_" << "UIview_test.txt";
	//write_test_data(UIview_test, nUsers, nItems, ss.str().c_str());
	//free(UIview_test); UIview_test = NULL;

	//ss.str("");
	//ss << nUsers << "_" << nItems << "_" << "UIview.txt";
	//write_UIview_data(UIview, nUsers, nItems, ss.str().c_str());

	clock_t end_of_read_dataset = clock();
	printf("read data set done. cost time:%.2f s\n", (double)(end_of_read_dataset - start_of_main) / CLOCKS_PER_SEC);
	
	///////////////////////////////////////
	///////////////////////////////////////
	//// for run parameters
	///////////////////////////////////////
	///////////////////////////////////////

	params.maxS = 5;
	params.minS = 1;
	params.num_of_kmeans = 3;
	params.num_of_LAE_iters = 10;
	int nas[11] = { 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120 };
	int nnas[11] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
	float hypers[13] = { 0.01, 0.03, 0.06, 0.1, 0.3, 0.6, 1, 3, 6, 10, 30, 60, 100 };
	params.num_of_anchors = 100;
	params.num_of_nearest_anchors = 5;
	float hyper = 0.03;
	params.alpha = hyper;
	params.beta = hyper;
	params.rho1 = hyper;
	params.rho2 = hyper;

	for (int ljs = 0; ljs < 11;ljs++){
		params.num_of_anchors = nas[ljs];
		printf("===========================\n ljs:%d num anchors:%d\n",ljs,params.num_of_anchors);
		std::string params_str;
		params_to_string(params, params_str);
	
		/*
		/////////////////////////////////////////////////////////
		//// 1. get anchors of uiview
		//// 2. get trans-Posi matrix of users uZ
		/////////////////////////////////////////////////////////

		CHECK_CUDA(cudaMalloc(&d_UIview, nUsers*nItems*sizeof(float)));
		CHECK_CUDA(cudaMalloc(&d_anchors_of_uiview, params.num_of_anchors*nItems*sizeof(float)));
		CHECK_CUDA(cudaMemcpy(d_UIview, UIview, nUsers*nItems*sizeof(float), cudaMemcpyHostToDevice));

		get_anchors_by_Kmeans(d_UIview, d_anchors_of_uiview, nItems, nUsers, params.num_of_kmeans, params.num_of_anchors);

		anchors_of_uiview = (float*)malloc(params.num_of_anchors*nItems*sizeof(float));
		CHECK_PARAM(anchors_of_uiview != NULL, "allocate space for anchors of uiview failed.");
		CHECK_CUDA(cudaMemcpy(anchors_of_uiview, d_anchors_of_uiview, params.num_of_anchors*nItems*sizeof(float), cudaMemcpyDeviceToHost));
		clock_t end_of_kmeans1 = clock();
		printf("get uiview anchors cost time:%.2f s\n", (double)(end_of_kmeans1 - start_of_main) / CLOCKS_PER_SEC);

		uZ = (float*)malloc(nUsers * params.num_of_anchors*sizeof(float));
		CHECK_PARAM(uZ != NULL, "allocate memory space for uZ failed.");

		CHECK_CUDA_STATUS(cublasCreate(&cublashandle));
		get_Z_by_LAE(UIview, d_UIview,
		anchors_of_uiview, d_anchors_of_uiview,
		nItems, nUsers, params.num_of_anchors,
		uZ,
		params.num_of_nearest_anchors,
		params.num_of_LAE_iters,
		cublashandle);
		CHECK_CUDA_STATUS(cublasDestroy(cublashandle));
		CHECK_CUDA(cudaFree(d_anchors_of_uiview)); d_anchors_of_uiview = NULL;
		free(anchors_of_uiview); anchors_of_uiview = NULL;
		CHECK_CUDA(cudaFree(d_UIview)); d_UIview = NULL;
		ss.str("");
		ss << nUsers << "_" << nItems << "_" << params_str << "_" << "uZ.txt";
		write_Z_into_file(uZ, params.num_of_anchors, nUsers, ss.str().c_str());
		free(uZ); uZ = NULL;

		clock_t end_of_uz = clock();
		printf("get uZ cost time:%.2f s\n", (double)(end_of_uz - end_of_kmeans1) / CLOCKS_PER_SEC);

		///////////////////////////////////////////////////////
		//// 1. get anchors of iuview
		//// 2. get trans-Posi matrix of items iZ
		///////////////////////////////////////////////////////

		CHECK_CUDA(cudaMalloc(&d_IUview, nItems*nUsers*sizeof(float)));
		CHECK_CUDA(cudaMemcpy(d_IUview, IUview, nItems*nUsers*sizeof(float), cudaMemcpyHostToDevice));
		CHECK_CUDA(cudaMalloc(&d_anchors_of_iuview, params.num_of_anchors*nUsers*sizeof(float)));

		get_anchors_by_Kmeans(d_IUview, d_anchors_of_iuview, nUsers, nItems, params.num_of_kmeans, params.num_of_anchors);

		anchors_of_iuview = (float*)malloc(params.num_of_anchors*nUsers*sizeof(float));
		CHECK_PARAM(anchors_of_iuview != NULL, "allocate space for anchors of iuview failed.");

		CHECK_CUDA(cudaMemcpy(anchors_of_iuview, d_anchors_of_iuview, params.num_of_anchors*nUsers*sizeof(float), cudaMemcpyDeviceToHost));
		clock_t end_of_kmeans2 = clock();
		printf("get iuview anchors cost time:%.2f s\n", (double)(end_of_kmeans2 - end_of_uz) / CLOCKS_PER_SEC);

		iZ = (float*)malloc(nItems * params.num_of_anchors*sizeof(float));
		CHECK_PARAM(iZ != NULL, "allocate memory space for iZ failed.");

		CHECK_CUDA_STATUS(cublasCreate(&cublashandle));
		get_Z_by_LAE(IUview, d_IUview,
		anchors_of_iuview, d_anchors_of_iuview,
		nUsers, nItems, params.num_of_anchors,
		iZ,
		params.num_of_nearest_anchors,
		params.num_of_LAE_iters,
		cublashandle);
		CHECK_CUDA_STATUS(cublasDestroy(cublashandle));

		CHECK_CUDA(cudaFree(d_anchors_of_iuview)); d_anchors_of_iuview = NULL;
		free(anchors_of_iuview); anchors_of_iuview = NULL;
		CHECK_CUDA(cudaFree(d_IUview)); d_IUview = NULL;

		ss.str("");
		ss << nUsers << "_" << nItems << "_" << params_str << "_" << "iZ.txt";
		write_Z_into_file(iZ, params.num_of_anchors, nItems, ss.str().c_str());
		free(iZ); iZ = NULL;
		clock_t end_of_iz = clock();
		printf("get iZ cost time:%.2f s\n", (double)(end_of_iz - end_of_kmeans2) / CLOCKS_PER_SEC);
		*/

		
		uZ = (float*)malloc(nUsers * params.num_of_anchors*sizeof(float));
		CHECK_PARAM(uZ != NULL, "allocate memory space for uZ failed.");

		ss.str("");
		ss << nUsers << "_" << nItems << "_" << params_str << "_" << "uZ.txt";
		read_Z_from_file(uZ, params.num_of_anchors, nUsers, ss.str().c_str());

		CHECK_CUDA(cudaMalloc(&d_S_u, nUsers*nUsers*sizeof(float)));
		CHECK_CUDA_STATUS(cublasCreate(&cublashandle));
		get_S_from_Z(d_S_u, uZ, nUsers, params.num_of_anchors, cublashandle);
		free(uZ);uZ=NULL;
		float alpha = params.rho1;
		CHECK_CUDA_STATUS(cublasSscal(cublashandle, nUsers*nUsers, &alpha, d_S_u, 1));
		CHECK_CUDA_STATUS(cublasDestroy(cublashandle));
		h_S_u = (float*)malloc(nUsers*nUsers*sizeof(float));
		CHECK_PARAM(h_S_u != NULL, "h_S_u failed!");
		CHECK_CUDA(cudaMemcpy(h_S_u, d_S_u, nUsers*nUsers*sizeof(float), cudaMemcpyDeviceToHost));
		CHECK_CUDA(cudaFree(d_S_u)); d_S_u = NULL;

		iZ = (float*)malloc(nItems * params.num_of_anchors*sizeof(float));
		CHECK_PARAM(iZ != NULL, "allocate memory space for iZ failed.");

		ss.str("");
		ss << nUsers << "_" << nItems << "_" << params_str << "_" << "iZ.txt";
		read_Z_from_file(iZ, params.num_of_anchors, nItems, ss.str().c_str());

		CHECK_CUDA(cudaMalloc(&d_S_i, nItems*nItems*sizeof(float)));
		CHECK_CUDA_STATUS(cublasCreate(&cublashandle));
		get_S_from_Z(d_S_i, iZ, nItems, params.num_of_anchors, cublashandle);
		free(iZ); iZ = NULL;
		alpha = params.rho2;
		CHECK_CUDA_STATUS(cublasSscal(cublashandle, nItems*nItems, &alpha, d_S_i, 1));
		CHECK_CUDA_STATUS(cublasDestroy(cublashandle));
		h_S_i = (float*)malloc(nItems*nItems*sizeof(float));
		CHECK_PARAM(h_S_i != NULL, "h_S_i failed!");
		CHECK_CUDA(cudaMemcpy(h_S_i, d_S_i, nItems*nItems*sizeof(float), cudaMemcpyDeviceToHost));
		CHECK_CUDA(cudaFree(d_S_i)); d_S_i = NULL;

		clock_t end_of_S = clock();
		printf("get S cost time:%.2f\n", (double)(end_of_S - start_of_main) / CLOCKS_PER_SEC);

		ss.str("");
		ss << nUsers << "_" << nItems << "_" << "UIview.txt";
		read_UIview_and_IUview_data(UIview, nUsers, nItems, IUview, ss.str().c_str());

		int x[5] = { 8, 16, 32, 64, 128 };
		for (int round = 0; round < 1; round++){
			printf("===================================\nround:%d %d bits\n", round, x[round]);
			params.r = x[round];
			// now we have S_i and S_u
			// next step is to calculate B and D by MVDCF
			// B is r-bits code of users: r X nusers
			// D is r-bits code of items: r X nitems
			float *B = (float *)malloc(params.r*nUsers*sizeof(float));
			CHECK_PARAM(B!=NULL,"allocate space for B failed.");
			float *D = (float *)malloc(params.r*nItems*sizeof(float));
			CHECK_PARAM(D!=NULL,"allocate space for D failed.");

			clock_t start_of_round = clock();
			MVDCF(B, D, params, UIview, IUview, h_S_i, h_S_u);

			clock_t end_of_mvdcf = clock();
			printf("mvdcf cost time %.2f\n", (double)(end_of_mvdcf - start_of_round) / CLOCKS_PER_SEC);


			ss.str("");
			ss << nUsers << "_" << nItems << "_" << params.r << "_" << params_str << "_" << "B_code.txt";
			write_into_file(B, params.r, nUsers, ss.str().c_str());

			ss.str("");
			ss << nUsers << "_" << nItems << "_" << params.r << "_" << params_str << "_" << "D_code.txt";
			write_into_file(D, params.r, nItems, ss.str().c_str());

			printf("write B and D into files done.\n");
			free(B); B = NULL;
			free(D); D = NULL;

		}
		clock_t end_of_main = clock();
		printf("total cost time %.2f\n", (double)(end_of_main - start_of_main) / CLOCKS_PER_SEC);

		free(h_S_i); h_S_i = NULL;
		free(h_S_u); h_S_u = NULL;
		
	}
	
	
	
	
	CHECK_CUDA(cudaDeviceReset());
	free(UIview);
	free(IUview);
	system("pause");
	return EXIT_SUCCESS; 
}